//    Copyright (C) Kherty.  All rights reserved.
#region
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Internals;

#endregion

namespace OpenLS.Spreadsheet
{
    partial class Range
    {
        public void Sort()
        {
            Sort(CellComparator.ComparePartialRowRangesUp, RowOrColumn.Row);
        }
        internal void Sort(Comparison<PartialRowOrColumnRange> comparison, RowOrColumn rc)
        {
            using (Worksheet.CreateUndoScope(StringTable.Sort))
            {
                var originalActiveCellAddress = Worksheet.ActiveCell.Address;
                if (Worksheet.Workbook.UndoEnabled)
                    Worksheet.Workbook.AddUndoUnit(new SortUndoUnit());
                var oldtoSort = new List<PartialRowOrColumnRange>(GetPartialRowOrColumnRanges(rc));
                var toSort = new List<RangeForSorting>(Enumerable.Select(GetPartialRowOrColumnRanges(rc), pp => new RangeForSorting(pp, Worksheet)));
                if (toSort.Count == 1)
                    return;
                toSort.Sort(
                    (x, y) =>
                        {
                            var result = comparison(x.Range, y.Range);
                            if (result != 0)
                                return result;
                            return x.Range.Index.CompareTo(y.Range.Index);
                        });
                var a = TopLeftCell.Address;
                foreach (var rangeForSorting in toSort)
                {
                    rangeForSorting.UnApply();
                }
                foreach (var rangeToSort in toSort)
                {
                    rangeToSort.Apply(a, rc);
                    ;
                    switch (rc)
                    {
                        case RowOrColumn.Row:
                            a = a.GetNeighbor(NavigationDirection.Down);
                            break;
                        case RowOrColumn.Column:
                            a = a.GetNeighbor(NavigationDirection.Right);
                            break;
                        default:
                            throw new NotSupportedException();
                    }
                }
                Worksheet.ActiveCell = Worksheet[originalActiveCellAddress];
            }
        }

        #region Nested type: CellDataForSorting
        private class CellDataForSorting
        {
            private readonly Comment _comment;
            private readonly List<ConditionalFormatting> _conditionalFormatting;
            private readonly DataBarInformation _dataBar;
            private readonly Expression _expression;
            private readonly Cell _originalCell;
            private CellFormattingRecord _styleRecord;
            private readonly RichTextParagraph _richValue;
            private readonly object _value;

            public CellDataForSorting(Cell originalCell)
            {
                Contract.Requires(originalCell != null);
                _originalCell = originalCell;
                _value = _originalCell.Value;
                _comment = _originalCell.Comment;
                _conditionalFormatting = _originalCell.ConditionalFormattings;
                _expression = _originalCell.Expression;
                _richValue = _originalCell.RichValue;
                _dataBar = _originalCell.DataBar;
                _styleRecord = _originalCell.FormattingRecord;
            }
            public Cell OriginalCell
            {
                get { return _originalCell; }
            }
            public void Apply(Cell cell)
            {
                var borders = cell.Borders;
                cell.SetFormattingRecordSafe(_styleRecord);
                cell.Borders = borders;
                cell.Comment = _comment;
                cell.ConditionalFormattings = _conditionalFormatting;
                cell.DataBar = _dataBar;
                cell.RichValue = _richValue;
                if (_expression != null)
                    cell.SetExpression(_expression.Offset(cell.Address - OriginalCell.Address));
                else
                    cell.Value = _value;
                
            }
            public void UnApply()
            {
                OriginalCell.Value = null;
                var borders = OriginalCell.Borders;
                OriginalCell.SetFormattingRecordSafe(new CellFormattingRecord());
                OriginalCell.Borders = borders;
                
                OriginalCell.Comment = null;
                OriginalCell.ConditionalFormattings = null;
                OriginalCell.DataBar = null;
                OriginalCell.RichValue = null;
                OriginalCell.SetExpression(null);
            }
        }
        #endregion

        #region Nested type: RangeForSorting
        private class RangeForSorting
        {
            private readonly List<CellDataForSorting> _cellData = new List<CellDataForSorting>();
            private readonly Worksheet _worksheet;

            public RangeForSorting(PartialRowOrColumnRange range, Worksheet worksheet)
            {
                _worksheet = worksheet;
                Range = range;
                foreach (var cell in range.GetNonEmptyCells())
                {
                    _cellData.Add(new CellDataForSorting(cell));
                }
            }
            public PartialRowOrColumnRange Range { get; set; }
            public void Apply(CellAddress address, RowOrColumn direction)
            {
                switch (direction)
                {
                    case RowOrColumn.Row:
                        foreach (var d in _cellData)
                        {
                            d.Apply(_worksheet[address.Row, d.OriginalCell.Column.Index]);
                        }
                        break;
                    case RowOrColumn.Column:
                        foreach (var d in _cellData)
                        {
                            d.Apply(_worksheet[d.OriginalCell.Row.Index, address.Column]);
                        }
                        break;
                    default:
                        throw new NotSupportedException();
                }
            }
            public void UnApply()
            {
                foreach (var dataForSorting in _cellData)
                {
                    dataForSorting.UnApply();
                }
            }
        }
        #endregion
    }
}